初次學習使用 React hooks 時,曾經不小心觸發無限的重新渲染迴圈然後暴打 API,心驚了一下,幸好 server 沒事XD 雖然那次的教訓讓我學會如何避免此類問題,但後來在其他情況下又遇到無限渲染的問題。這說明對於 React hooks 的使用還不夠熟悉,尤其是 useState
與 useEffect
的觀念不夠熟悉,那這次鐵人賽就特別再多查一些可能會觸發的情況,然後整理起來~
function App() {
const [count, setCount] = useState(0);
setCount(1); // infinite loop
return ...
}
造成原因:
React 在每次渲染時,若偵測到狀態的變化就會觸發 re-render,而 setState
每次都會導致狀態更新。因此,若在元件的渲染過程中直接執行 setCount
,就會進入無限的渲染迴圈。
正確寫法:
應將 setState
放在 useEffect
中,並使用空的 dependencies
陣列,確保只在初次渲染時更新狀態。
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(1);
}, [])
return ...
}
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1) // infinite loop
}, [count])
return ...
}
造成原因:
這個問題出現在 useEffect
的 dependencies
中直接依賴 count
,當 setCount
更新 count
後,會觸發元件重新渲染,進而再次執行 useEffect
,這樣的循環會導致無限的渲染。
正確寫法:
在 setCount
中使用 Day 23 時提到的「傳入更新函式」來更新狀態,確保狀態更新基於前一個值,而不依賴當前的 count
。
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(previousCount => previousCount + 1)
}, [])
return ...
}
export default function App() {
const [count, setCount] = useState(0);
return (
<button onClick={setCount(1)}>Submit</button> // infinite loop
);
}
造成原因:
在這個例子中,setCount(1)
會在每次元件渲染時立即執行,而不是等待點擊事件觸發。因此,每次渲染時都會更新 count
,從而導致無限的重新渲染。
正確寫法:
將 setCount
包裝在箭頭函式中,確保它只在按鈕點擊時才執行。
export default function App() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(1)}>Submit</button> // infinite loop
);
}
function App() {
const [count, setCount] = useState(0);
let unstableValue = Math.random(); // 每次渲染都會變化
useEffect(() => {
console.log('unstableValue 改變時執行');
}, [unstableValue]); // 依賴於不穩定的變數
return ...
}
造成原因:
每次渲染時,unstableValue
都會生成新的值,這會導致 useEffect
被不斷觸發,從而產生無限的副作用執行迴圈。
正確寫法:
useEffect
的依賴應該是穩定且可以預期變化的變數,通常是 useState
或 useReducer
管理的狀態。
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('count 改變時執行');
}, [count]); // 依賴穩定的狀態變數
return ...
}